Импорт данных MNIST

(* необходимо наличие в папке с ноутбуком файлов MNIST-images и MNIST-labels! *)

SetDirectory@NotebookDirectory[];

(* парсинг файла с изображениями *)
rawimgs=ImportByteArray[ReadByteArray["MNIST-images"]];
read32[data_,off_]:=data[[#+1;;#+4]].Reverse[Power[2,8(Range[4]-1)]]&[off];
{mn,npics,nrows,ncols}=read32[rawimgs,#]&/@Range[0,12,4];
in=Partition[rawimgs[[17;;]]/255., nrows ncols]; (* преобразование изображений во входные вектора *)
imgs=Image[Partition[#,ncols]]&/@in;

(* парсинг файла с метками *)
rawlabs=ImportByteArray[ReadByteArray["MNIST-labels"]];
{mnl,nlabs}=read32[rawlabs,#]&/@Range[0,4,4];
labels=rawlabs[[9;;]];

(* превью импортированных изображений и меток *)
imgs[[;;30]]//GraphicsRow
labels[[;;30]]

Параметры нейросети

(* Функция активации (Сигмоида) *)
σ[x_]:=1/(1.+Exp[-x]);
(* Производная функции активации *)
dσ[x_]:=σ[x]/(1+Exp[x]);
(* Функции для каждого из слоёв *)
f=σ;df=dσ; 
g=σ;dg=dσ;

(* Количество нейронов в слоях *)
Ni=nrows ncols; (* вектор изображения *)
N1=28; (* скрытый слой *)
N0=10;  (* выходной слой (унитарный код цифры) *)

(* Инициализация весов и смещений *)
u=Array[RandomReal[{-1,1}]&,N0];
v=Array[RandomReal[{-1,1}]&,N1];
U=Array[RandomReal[{-1,1}]&,{N0,N1}];
V=Array[RandomReal[{-1,1}]&,{N1,Ni}];

Обучение нейросети

(* Метапараметры *)
batchsize=100; (* размер пакета *)
α=5.; (* скорость градиентного спуска *)

batches=Partition[{in,labels}\[Transpose],batchsize]; (* пакеты с парами изображение(вектор)/метка *)
dt=1./Length[batches]; 

(* вычисление градиента ошибки *)
BackProp[{u_,v_,U_,V_}]:=Function[{i,λ},Module[{dedu,dedv,dedU,dedV,dedb,l,ol,y,b,z,o},

(* Forward Pass *)
y=V.i+v;
b=g[y];
z=U.b+u;
o=f[z];

l=IdentityMatrix[10][[λ+1]]; (* конвертация цифры в унитарный код *)

(* Back Propagation *)
ol=(o-l);
dedu=ol*df[z]; (* ε=(o-l).(o-l); dedu=dedz; dodz=dfdz=df *)
dedU=dedu ⊗ b;
dedb=U\[Transpose].dedu;
dedv =dedb*dg[y]; (* dedv=dedy; dbdy=dgdy=dg *)
dedV=dedv ⊗ i;

{{dedu,dedv,dedU,dedV},ol.ol} (* возвращаемое значение {((∂m)/(δε)), 2ε} *)
]];

(* шаг стохастического градиентного спуска *)
GradStep[uvUV_,batch_]:=Module[{grade,res},

grade=Total[BackProp[uvUV]@@@batch]/batchsize; (* среднее по пакету значение градиента ошибки *)
epoch+=dt; (* текущий прогресс обучения *)
AppendTo[elist,{epoch,grade[[2]]}]; (* лог обучения *)
uvUV-α grade[[1]] (* изменение параметров нейросети *)

];

(* Инициализация параметров обучения *)
epoch=0;
maxEpoch=3; (* длительность обучения (количество полных проходов по обучающей выборке) *)
elist={{0,1}}; (* начальная точка в логе *)

(* Обучение *)
Print[Dynamic[epoch]]
Dynamic[ListLinePlot[elist,PlotStyle->Black,PlotRange->{0,1}],SaveDefinitions->True] (* график лога обучения *)
model=Nest[Fold[GradStep,#,batches]&,{u,v,U,V},maxEpoch]; (* Цикл обучения *)

Тест результатов обучения

(* функции с предыдущего семинара *)
ForwPass[i_]:=Module[{u,v,U,V,y,b,z,o},
{u,v,U,V}=model;
y=V.i+v;
b=g[y];
z=U.b+u;
f[z]
];

OCR[img_]:=Module[{x,out},
x=Flatten@ImageData@ColorConvert[img,"Grayscale"];
out=ForwPass[x];
First@Ordering[out,-1]-1 
];

(* функции для графического отображения статистики распознавания *)
OCRcell=Graphics[{
White,Opacity[#[[2]]],Text[Style[#[[1]],FontSize->50,FontFamily->"Arial"],{0,.15}],
Opacity[.5],Text[Style[ToString@Round[100 #[[2]]]<>"%",FontSize->14,FontFamily->"Arial"],{0,-.65}]
},PlotRange->{{-1,1},1.1{-1,1}},Background->Black]&;

OCRstat[img_]:=GraphicsRow[Prepend[OCRcell/@SortBy[{Range[0,9],((#)/(Total[#]))&@ForwPass[img//ImageData//Flatten] }\[Transpose],-#[[2]]&][[;;4]],img],Spacings->0,ImageSize->300,Background->Black];
OCRstat[imgs__]:=OCRstat/@List[imgs];
OCRstat[imgs_List]:=OCRstat/@imgs;

(* Распознавание изображений из обучающей выборки *)
OCRstat[{,,,,,,,,,}]//TableForm

(* Распознавание новых изображений *)
OCRstat[{,,,,,,,,,}]//TableForm